﻿@using Gurux.DLMS.AMI.Shared.DIs

@using Gurux.Net
@using Gurux.Serial
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Gurux.DLMS.AMI.Shared.DTOs
@using Gurux.DLMS.AMI.Shared.Rest
@using Gurux.DLMS
@using Gurux.DLMS.ManufacturerSettings
@using Gurux.DLMS.Objects
@using System.Xml.Linq
@using System.IO
@using System.Xml
@using System.Xml.Serialization
@using Gurux.DLMS.AMI.Shared.Enums
@using Gurux.DLMS.AMI.Client.Helpers
@using Gurux.DLMS.AMI.Module
@using Gurux.DLMS.AMI.Client.Pages.Media
@using System.Text.Json
@using System.Text.Encodings.Web
@using System.Text.Json.Serialization
@using System.Diagnostics
@using System.Text;

@attribute [Authorize(Roles = "Admin, Device, DeviceManager")]
@inject NavigationManager NavigationManager
@inject HttpClient Http
@inject IGXNotifier2 Notifier
@implements IDisposable
<CascadingValue Value="this">

    @if (Active != null)
    {
        @if (Parent?.Templates != null && Parent.Templates.Any())
        {
            @if (Notifier.Action != CrudAction.Create)
            {
                <MenuControl RightCorner="true" Bottom="true">
                    <ChildContent>
                        <MenuItem Text="Save as default" Icon="oi oi-pencil" OnClick="@(async () => await OnDefault())" />
                    </ChildContent>
                </MenuControl>
            }
            <EditForm Model="@Active">
                <DataAnnotationsValidator />
                <GXValidator @ref="_validator" OnValidate="OnValidate" />
                <ul>
                    @foreach (var message in context.GetValidationMessages())
                    {
                        <li class="validation-message">@message</li>
                    }
                </ul>
                @if (action == CrudAction.Create)
                {
                    <div class="form-group row">
                        <div class="form-inline">
                            <label>立即创建设备.</label>
                            <InputSwitch @bind-Value="CreateImmediately">
                            </InputSwitch>
                        </div>
                    </div>
                }
                <div class="row justify-content-center">
                    <div>
                        @if (Parent.Manufacturers != null && Parent.Manufacturers.Any())
                        {
                            <div class="form-group row">
                                <label>@Properties.Resources.Manufacturer</label>
                                <InputSelect id="type" class="form-control"
                                @bind-Value="SelectedManufacturer" disabled="@IsCreate()">
                                    @foreach (var it in Parent.Manufacturers)
                                    {
                                        <option value="@it.Id">@it.Name</option>
                                    }
                                </InputSelect>
                            </div>
                        }
                        @if (_manufacturer != null && _manufacturer.Id != Guid.Empty)
                        {
                            @if (_manufacturer?.Models != null)
                            {
                                <div class="form-group row">
                                    <label>@Properties.Resources.Model</label>
                                    <InputSelect id="type" class="form-control"
                                    @bind-Value="SelectedModel" disabled="@IsCreate()">
                                        @foreach (var it in _manufacturer.Models)
                                        {
                                            <option value="@it.Id">@it.Name</option>
                                        }
                                    </InputSelect>
                                </div>
                                @if (_model?.Versions != null)
                                {
                                    <div class="form-group row">
                                        <label>@Properties.Resources.Version</label>
                                        <InputSelect id="type" class="form-control"
                                        @bind-Value="SelectedVersion" disabled="@IsCreate()">
                                            @foreach (var it in _model.Versions)
                                            {
                                                <option value="@it.Id">
                                                    @(string.IsNullOrEmpty(it.Name) ? Properties.Resources.Generic : it.Name)
                                                </option>
                                            }
                                        </InputSelect>
                                    </div>
                                    <div class="form-group row">
                                        <label>@Properties.Resources.Name</label>
                                        <InputText id="name" class="form-control"
                                        @bind-Value="Active.Name" />
                                        <ValidationMessage For="@(() => Active.Name)" />
                                    </div>
                                    @if (ShowLogicalDeviceName)
                                    {
                                        <div class="form-group row">
                                            <label>@Properties.Resources.LogicalDeviceName</label>
                                            <InputText id="sn" class="form-control"
                                            @bind-Value="LogicalDeviceName" />
                                            <ValidationMessage For="@(() => LogicalDeviceName)" />
                                        </div>
                                    }
                                    @if (_version?.Settings != null)
                                    {
                                        <div class="form-group row">
                                            <label>@Properties.Resources.InterfaceType</label>
                                            <InputSelect id="type" class="form-select"
                                            @bind-Value="SelectedSettings" disabled="@IsCreate()">
                                                @foreach (var it in GetInterfaceTypes())
                                                {
                                                    <option value="@it.Id">@GetInterfaceType(it)</option>
                                                }
                                            </InputSelect>
                                        </div>
                                    }
                                }
                            }
                        }
                        else
                        {
                            <div class="form-group row">
                                <label>@Properties.Resources.Type</label>
                                <InputSelect id="type" class="form-control"
                                @bind-Value="SelectedTemplate" disabled="@IsCreate()">
                                    @foreach (var it in GetCustomTemplates())
                                    {
                                        <option value="@it.Name">@it.Name</option>
                                    }
                                </InputSelect>
                            </div>
                            @if (_templateSettings != null)
                            {
                                <div class="form-group row">
                                    <label>@Properties.Resources.Manufacturer</label>
                                    <InputText id="manuacturer" class="form-control" readonly="readonly"
                                    @bind-Value="_templateSettings.Manufacturer" />
                                </div>
                            }
                            <div class="form-group row">
                                <label>@Properties.Resources.Name</label>
                                <InputText id="Name" class="form-control"
                                @bind-Value="Active.Name" />
                                <ValidationMessage For="@(() => Active.Name)" />
                            </div>
                            @if (ShowLogicalDeviceName)
                            {
                                <div class="form-group row">
                                    <label>@Properties.Resources.LogicalDeviceName</label>
                                    <InputText id="sn" class="form-control"
                                    @bind-Value="LogicalDeviceName" />
                                    <ValidationMessage For="@(() => LogicalDeviceName)" />
                                </div>
                            }
                            @if (_settings != null)
                            {
                                <div class="form-group row">
                                    <label style="margin:auto">@Properties.Resources.InterfaceType</label>
                                    <select class="form-select" @bind="@_settings.InterfaceType">
                                        @foreach (var it in InterfaceList)
                                        {
                                            <option value="@Convert.ToInt32(it)">@it</option>
                                        }
                                    </select>
                                </div>
                            }
                        }
                        <div class="form-group row">
                            <label>@Properties.Resources.Authentication</label>
                            <InputSelect id="type" class="form-select"
                            @bind-Value="SelectedSettings" disabled="@IsCreate()">
                                @foreach (var it in GetAuthenticationLevels())
                                {
                                    <option value="@it.Id">@GetAuthenticationLevel(it)</option>
                                }
                            </InputSelect>
                        </div>
                        @if (_settings != null)
                        {
                            <fieldset disabled=@DisablePassword()>
                                <div class="form-group row">
                                    <div class="form-inline">
                                        <label>@Properties.Resources.Password</label>
                                        <InputSwitch OnChange="OnHexChange"
                                        @bind-Value="_settings.IsHex">
                                            @Properties.Resources.Hex
                                        </InputSwitch>
                                    </div>
                                    <InputText id="password" class="form-control"
                                    @bind-Value="Password" disabled="@IsAuthenticated()" />
                                    <ValidationMessage For="@(() => Password)" />
                                </div>
                            </fieldset>
                        }
                        <fieldset disabled=@DisableSecurity()>
                            <div class="form-group row">
                                <label>@Properties.Resources.Security</label>
                                <InputSelect id="type" class="form-select"
                                @bind-Value="SelectedSettings" disabled="@IsCreate()">
                                    @foreach (var it in GetSecurityLevels())
                                    {
                                        <option value="@it.Id">@GetSecurity(it)</option>
                                    }
                                </InputSelect>
                            </div>
                            @if (_settings != null)
                            {
                                <div class="form-group row">
                                    <InputHexAscii @ref="_clientSystemTitle"
                                    @bind-Value="@ClientSystemTitle"
                                                   Title="@Properties.Resources.ClientSystemTitle">
                                    </InputHexAscii>
                                    <ValidationMessage For="@(() => _clientSystemTitle)" />
                                </div>
                                <div class="form-group row">
                                    <InputHexAscii @ref="_deviceSystemTitle"
                                    @bind-Value="@DeviceSystemTitle"
                                                   Title="@Properties.Resources.DeviceSystemTitle">
                                    </InputHexAscii>
                                    <ValidationMessage For="@(() => _deviceSystemTitle)" />
                                </div>
                                <div class="form-group row">
                                    <InputHexAscii @ref="_blockCipherKey"
                                    @bind-Value="@BlockCipherKey"
                                                   Title="@Properties.Resources.BlockCipherKey">
                                    </InputHexAscii>
                                    <ValidationMessage For="@(() => _blockCipherKey)" />
                                </div>
                                <div class="form-group row">
                                    <InputHexAscii @ref="_authenticationKey"
                                    @bind-Value="@AuthenticationKey"
                                                   Title="@Properties.Resources.AuthenticationKey">
                                    </InputHexAscii>
                                </div>
                                <ValidationMessage For="@(() => _authenticationKey)" />
                            }
                        </fieldset>
                        @if (_settings != null && _settings.HDLCAddressing == (int)HDLCAddressType.SerialNumber)
                        {
                            <div class="form-group row">
                                <label>@Properties.Resources.SerialNumber</label>
                                <InputNumber id="resend" class="form-control"
                                @bind-Value="_settings.PhysicalAddress" />
                                <ValidationMessage For="@(() => _settings.PhysicalAddress)" />
                            </div>
                        }
                        <div class="form-group row">
                            <label>@Properties.Resources.WaitTime</label>
                            <InputIntegerTimeSpan id="resend" class="form-control"
                            @bind-Value="Active.WaitTime" />
                            <ValidationMessage For="@(() => Active.WaitTime)" />
                        </div>
                        <div class="form-group row">
                            <label>Resend count</label>
                            <InputNumber id="resend" class="form-control"
                            @bind-Value="Active.ResendCount" />
                            <ValidationMessage For="@(() => Active.ResendCount)" />
                        </div>
                        <div class="form-group row">
                            <InputNullableSwitch Text="@Properties.Resources.Dynamic"
                            @bind-Value="Active.Dynamic" />
                            <br />
                        </div>
                        @if (!Active.Dynamic.GetValueOrDefault(false))
                        {
                            <div class="form-group row">
                                <label style="margin:auto">@Properties.Resources.Media</label>
                                <select class="form-select" @bind="@Active.MediaType">
                                    @foreach (var it in MediaList)
                                    {
                                        <option value="@it.Key">@it.Value</option>
                                    }
                                </select>
                            </div>
                            @if (Active.MediaType == typeof(GXNet).FullName)
                            {
                                <NetworkSettings @bind-Settings="@Active.MediaSettings" @ref=networkSettings></NetworkSettings>
                            }
                            @if (Active.MediaType == typeof(GXSerial).FullName || string.IsNullOrEmpty(Active.MediaType))
                            {
                                <SerialSettings @bind-Settings="@Active.MediaSettings" @ref=serialSettings></SerialSettings>
                            }
                        }
                        <div class="form-group row">
                            <label>@Properties.Resources.ConnectionUpTime</label>
                            <InputNullableIntegerTimeSpan id="resend" class="form-control"
                            @bind-Value="Active.ConnectionUpTime" />
                            <ValidationMessage For="@(() => Active.ConnectionUpTime)" />
                        </div>
                        <ParametersView @ref="ParametersView" Parameters="@Parameters" Target="@Active"></ParametersView>
                        <div class="form-group row">
                            <label>@Properties.Resources.TraceLevels</label>
                            <NullableTraceLevelSelector @bind-Value="Active.TraceLevel">
                            </NullableTraceLevelSelector>
                        </div>
                    </div>
                    @if (Active.CreationTime != DateTime.MinValue)
                    {
                        <div class="form-group row">
                            <label>@Properties.Resources.CreationTime</label>
                            <InputDate Type="@InputDateType.DateTimeLocal" id="generation" readonly="readonly" class="form-control"
                            @bind-Value="Active.CreationTime" />
                        </div>
                    }
                    @if (Active.Updated != null)
                    {
                        <div class="form-group row">
                            <label>@Properties.Resources.Updated</label>
                            <InputDate Type="@InputDateType.DateTimeLocal" id="generation" readonly="readonly" class="form-control"
                            @bind-Value="Active.Updated" />
                        </div>
                    }
                    @if (Active.Removed != null)
                    {
                        <div class="form-group row">
                            <label>@Properties.Resources.Removed</label>
                            <InputDate Type="@InputDateType.DateTimeLocal" id="removed" class="form-control"
                            @bind-Value="Active.Removed" />
                        </div>
                    }
                    @if (action == CrudAction.Delete)
                    {
                        <hr />
                        <p>
                        </p>
                        <div class="form-group row">
                            <InputRadioGroup @bind-Value="DeleteTarget">
                                <InputRadio Value="false">
                                </InputRadio>
                                <label>@Properties.Resources.TargetDisableQuestion</label><br>
                                <InputRadio Value="true">
                                </InputRadio>
                                <label>@Properties.Resources.TargetDeleteQuestion</label>
                            </InputRadioGroup>
                            <br />
                        </div>
                    }
                </div>
                <br />
            </EditForm>
        }
    }
</CascadingValue>
@code {
    private InputHexAscii? _clientSystemTitle;
    private InputHexAscii? _deviceSystemTitle;
    private InputHexAscii? _blockCipherKey;
    private InputHexAscii? _authenticationKey;

    /// <summary>
    /// Is device created immediatly or when needed.
    /// </summary>
    /// <remarks>
    /// Create devices immediatly is taking longer time when it's added.
    /// This is handy when a large amount of the meter is added.
    /// </remarks>
    private bool CreateImmediately { get; set; }

    [CascadingParameter]
    private DeviceTab? Parent { get; set; }

    private GXDevice? _active;

    public GXDevice? Active
    {
        get
        {
            if (_active != null)
            {
                return _active;
            }
            return Parent?.Active;
        }
    }

    private bool ShowLogicalDeviceName
    {
        get
        {
            GXObject? ldn = Active?.Objects?.Where(w => w.Template.LogicalName == "0.0.42.0.0.255").SingleOrDefault() as GXObject;
            return ldn?.Attributes?.FirstOrDefault() is GXAttribute;
        }
    }

    private string? LogicalDeviceName
    {
        get
        {
            GXObject? ldn = Active?.Objects?.Where(w => w.Template.LogicalName == "0.0.42.0.0.255").SingleOrDefault() as GXObject;
            if (ldn?.Attributes?.FirstOrDefault() is GXAttribute att)
            {
                return att.Value;
            }
            return null;
        }
        set
        {
            GXObject? ldn = Active?.Objects?.Where(w => w.Template.LogicalName == "0.0.42.0.0.255").SingleOrDefault() as GXObject;
            if (ldn?.Attributes?.FirstOrDefault() is GXAttribute att)
            {
                att.Value = value;
            }
        }
    }

    private byte[]? ClientSystemTitle
    {
        get
        {
            if (string.IsNullOrEmpty(_settings?.ClientSystemTitle))
            {
                return null;
            }
            return GXDLMSTranslator.HexToBytes(_settings.ClientSystemTitle);

        }
        set
        {
            if (_settings != null)
            {
                _settings.ClientSystemTitle = GXDLMSTranslator.ToHex(value, false);
            }
        }
    }

    private byte[]? DeviceSystemTitle
    {
        get
        {
            if (string.IsNullOrEmpty(_settings?.DeviceSystemTitle))
            {
                return null;
            }
            return GXDLMSTranslator.HexToBytes(_settings.DeviceSystemTitle);

        }
        set
        {
            if (_settings != null)
            {
                _settings.DeviceSystemTitle = GXDLMSTranslator.ToHex(value, false);
            }
        }
    }

    private byte[]? BlockCipherKey
    {
        get
        {
            if (string.IsNullOrEmpty(_settings?.BlockCipherKey))
            {
                return null;
            }
            return GXDLMSTranslator.HexToBytes(_settings.BlockCipherKey);

        }
        set
        {
            if (_settings != null)
            {
                _settings.BlockCipherKey = GXDLMSTranslator.ToHex(value, false);
            }
        }
    }

    private byte[]? AuthenticationKey
    {
        get
        {
            if (string.IsNullOrEmpty(_settings?.AuthenticationKey))
            {
                return null;
            }
            return GXDLMSTranslator.HexToBytes(_settings.AuthenticationKey);

        }
        set
        {
            if (_settings != null)
            {
                _settings.ClientSystemTitle = GXDLMSTranslator.ToHex(value, false);
            }
        }
    }

    private bool DisablePassword()
    {
        if (_settings != null &&
            _settings.Authentication != (byte)Gurux.DLMS.Enums.Authentication.None)
        {
            return action != CrudAction.Create;
        }
        return true;
    }


    private bool DisableSecurity()
    {
        if (_settings != null &&
            (_settings.Authentication == (byte)Gurux.DLMS.Enums.Authentication.HighGMAC ||
            _settings.Security != (byte)Gurux.DLMS.Enums.Security.None))
        {
            return action != CrudAction.Create;
        }
        return true;
    }

    private bool IsHexDisabled()
    {
        if (IsAuthenticated())
        {
            return true;
        }
        if (_settings?.HexPassword == null || !_settings.IsHex)
        {
            return false;
        }
        return !IsAsciiString(_settings.HexPassword);
    }

    private byte[] GetByteArray(string? value, bool toHex)
    {
        if (string.IsNullOrEmpty(value))
        {
            return new byte[0];
        }
        if (toHex)
        {
            //Convert ASCII to hex.
            return ASCIIEncoding.ASCII.GetBytes(value);
        }
        //Convert hex to ASCII.
        return GXDLMSTranslator.HexToBytes(value);
    }

    private string? GetValue(string? value, bool toHex)
    {
        byte[] bytes = GetByteArray(value, toHex);
        if (toHex)
        {
            //Convert ASCII to hex.
            return GXDLMSTranslator.ToHex(bytes);
        }
        //Convert hex to ASCII.
        return ASCIIEncoding.ASCII.GetString(bytes);
    }

    /// <summary>
    /// User changes password type from ASCII to HEX or vice versa.
    /// </summary>
    /// <param name="value"></param>
    private void OnHexChange(bool value)
    {
        try
        {
            if (_settings != null)
            {
                _settings.Password = GetValue(_settings.Password, value);
                if (value)
                {
                    //Convert ASCII to hex.
                    _settings.Password = null;
                }
                else
                {
                    //Convert hex to ASCII.
                    _settings.HexPassword = null;
                }
            }
        }
        catch (Exception ex)
        {
            _validator?.AddError(() => Password, ex.Message);
        }
    }



    /// <summary>
    /// Check is byte buffer ASCII string.
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    [System.Diagnostics.DebuggerStepThrough]
    public static bool IsAsciiString(byte[] value)
    {
        if (value != null)
        {
            foreach (byte it in value)
            {
                if (it < 32 || it > 127)
                {
                    return false;
                }
            }
        }
        return true;
    }

    /// <summary>
    /// Find only custom templates from the device templates and remove manufacturer templates.
    /// </summary>
    /// <returns>Collection of custom templates.</returns>
    private IEnumerable<GXDeviceTemplate> GetCustomTemplates()
    {
        if (Parent?.CustomTemplates == null)
        {
            return new GXDeviceTemplate[0];
        }
        return Parent.CustomTemplates;
    }

    private GXValidator? _validator;


    private void SelectFirstManufacturer()
    {
        if (Active != null && Parent?.Manufacturers != null && Parent.Manufacturers.Any())
        {
            SelectedManufacturer = Parent.Manufacturers[0].Id;
            if (_deviceSettings?.Template != null && Parent?.Templates != null && Parent.Templates.Any())
            {
                Active.Template = Parent.Templates.Where(w => w.Id == _deviceSettings.Template.Id).SingleOrDefault();
            }
        }
    }

    private void SelectFirstCustomTemplate()
    {
        if (Active != null && Parent?.CustomTemplates != null && Parent.CustomTemplates.Any())
        {
            if (SelectedManufacturer == null ||
                SelectedManufacturer == Guid.Empty)
            {
                SelectedTemplate = Parent.CustomTemplates[0].Name;
            }
            else
            {
                SelectedTemplate = null;
            }
        }
    }
    /// <summary>
    /// Device tab asks to update the UI.
    /// </summary>
    public void Update()
    {
        if (Active != null)
        {
            //New device is created.
            if (Active.Template == null || Active.Template.Id == Guid.Empty)
            {
                //Select first manufacturer.
                SelectFirstManufacturer();
                //The first custom teplate is selected is manufacturer is not selected.
                SelectFirstCustomTemplate();
            }
            else if (Parent?.Manufacturers != null && Parent.Manufacturers.Any())
            {
                //Find manufacturer.
                bool found = false;
                foreach (var manufacturer in Parent.Manufacturers)
                {
                    if (manufacturer.Models != null)
                    {
                        foreach (var model in manufacturer.Models)
                        {
                            if (model.Versions != null)
                            {
                                foreach (var version in model.Versions)
                                {
                                    if (version.Settings != null)
                                    {
                                        foreach (var settings in version.Settings)
                                        {
                                            if (settings?.Template?.Id == Active.Template.Id)
                                            {
                                                _manufacturer = manufacturer;
                                                _model = model;
                                                _version = version;
                                                _deviceSettings = settings;
                                                settings.Settings = Active.Settings;
                                                found = true;
                                                break;
                                            }
                                        }
                                    }
                                    if (found)
                                    {
                                        break;
                                    }
                                }
                                if (found)
                                {
                                    break;
                                }
                            }
                            if (found)
                            {
                                break;
                            }
                        }
                        if (found)
                        {
                            break;
                        }
                    }
                    if (found)
                    {
                        break;
                    }
                }
            }
            if (Active.Template != null
                && !string.IsNullOrEmpty(Active.Template.Settings))
            {
                _templateSettings = JsonSerializer.Deserialize<Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings>(Active.Template.Settings);
            }
            if (!string.IsNullOrEmpty(Active.Settings))
            {
                _settings = JsonSerializer.Deserialize<Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings>(Active.Settings);
            }
            else
            {
                //Use template settings as default meter settings when the meter is created.
                _settings = _templateSettings;
            }
            if (string.IsNullOrEmpty(Active.MediaType))
            {
                Active.MediaType = typeof(GXNet).FullName;
            }
            if (Active?.Parameters != null)
            {
                Parameters.AddRange(Active.Parameters);
            }
        }
        StateHasChanged();
    }

    /// <summary>
    /// Get available interface types.
    /// </summary>
    /// <returns>Available interface types.</returns>
    List<Gurux.DLMS.AMI.Shared.DTOs.Manufacturer.GXDeviceSettings> GetInterfaceTypes()
    {
        Gurux.DLMS.Enums.InterfaceType current = Gurux.DLMS.Enums.InterfaceType.HDLC;
        if (_deviceSettings != null)
        {
            current = GetInterfaceType(_deviceSettings);
        }
        List<Gurux.DLMS.AMI.Shared.DTOs.Manufacturer.GXDeviceSettings> settings = new();
        List<Gurux.DLMS.Enums.InterfaceType> items = new List<Enums.InterfaceType>();
        if (_version?.Settings != null)
        {
            foreach (var it in _version.Settings)
            {
                var item = GetInterfaceType(it);
                if (!items.Contains(item) && (_deviceSettings == null || it.Id == _deviceSettings.Id || item != current))
                {
                    settings.Add(it);
                    items.Add(item);
                }
            }
        }
        return settings;
    }

    /// <summary>
    /// Get available authentication levels.
    /// </summary>
    /// <returns>Available authentication levels.</returns>
    List<Gurux.DLMS.AMI.Shared.DTOs.Manufacturer.GXDeviceSettings> GetAuthenticationLevels()
    {
        Gurux.DLMS.Enums.Authentication current = Gurux.DLMS.Enums.Authentication.None;
        if (_templateSettings != null)
        {
            //Get default value when new device is create from device template.
            current = (Gurux.DLMS.Enums.Authentication)_templateSettings.Authentication;
        }
        if (_deviceSettings != null)
        {
            current = GetAuthenticationLevel(_deviceSettings);
        }
        List<Gurux.DLMS.AMI.Shared.DTOs.Manufacturer.GXDeviceSettings> settings = new();
        List<Gurux.DLMS.Enums.Authentication> items = new List<Enums.Authentication>();
        if (_version?.Settings != null)
        {
            foreach (var it in _version.Settings)
            {
                var item = GetAuthenticationLevel(it);
                if (!items.Contains(item) && (_deviceSettings == null || it.Id == _deviceSettings.Id || item != current))
                {
                    settings.Add(it);
                    items.Add(item);
                }
            }
        }
        else if (_deviceSettings != null)
        {
            settings.Add(_deviceSettings);
        }
        else
        {
            settings.Add(new AMI.Shared.DTOs.Manufacturer.GXDeviceSettings()
                {
                    Settings = JsonSerializer.Serialize(new Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings()
                    {
                        Authentication = (byte)current
                    })
                });
        }
        return settings;
    }

    /// <summary>
    /// Get available security levels.
    /// </summary>
    /// <returns>Available security levels.</returns>
    List<Gurux.DLMS.AMI.Shared.DTOs.Manufacturer.GXDeviceSettings> GetSecurityLevels()
    {
        Gurux.DLMS.Enums.Security current = Gurux.DLMS.Enums.Security.None;
        if (_templateSettings != null)
        {
            //Get default value when new device is create from device template.
            current = (Gurux.DLMS.Enums.Security)_templateSettings.Security;
        }
        if (_deviceSettings != null)
        {
            current = GetSecurity(_deviceSettings);
        }
        List<Gurux.DLMS.AMI.Shared.DTOs.Manufacturer.GXDeviceSettings> settings = new();
        List<Gurux.DLMS.Enums.Security> items = new List<Enums.Security>();
        if (_version?.Settings != null)
        {
            foreach (var it in _version.Settings)
            {
                var item = GetSecurity(it);
                if (!items.Contains(item) && (_deviceSettings == null || it.Id == _deviceSettings.Id || item != current))
                {
                    settings.Add(it);
                    items.Add(item);
                }
            }
        }
        else if (_deviceSettings != null)
        {
            settings.Add(_deviceSettings);
        }
        else
        {
            //Default authentication is added.
            settings.Add(new AMI.Shared.DTOs.Manufacturer.GXDeviceSettings()
                {
                    Settings = JsonSerializer.Serialize(new Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings()
                    {
                        Security = (byte)current
                    })
                });
        }
        return settings;
    }


    Gurux.DLMS.Enums.InterfaceType GetInterfaceType(AMI.Shared.DTOs.Manufacturer.GXDeviceSettings value)
    {
        return (Gurux.DLMS.Enums.InterfaceType)JsonSerializer.Deserialize<Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings>(value.Settings).InterfaceType;
    }

    Gurux.DLMS.Enums.Security GetSecurity(AMI.Shared.DTOs.Manufacturer.GXDeviceSettings value)
    {
        return (Gurux.DLMS.Enums.Security)JsonSerializer.Deserialize<Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings>(value.Settings).Security;
    }

    Gurux.DLMS.Enums.Authentication GetAuthenticationLevel(AMI.Shared.DTOs.Manufacturer.GXDeviceSettings value)
    {
        return (Gurux.DLMS.Enums.Authentication)JsonSerializer.Deserialize<Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings>(value.Settings).Authentication;
    }

    /// <summary>
    /// User action.
    /// </summary>
    [Parameter]
    public string? Action { get; set; }

    private CrudAction action;
    /// <summary>
    /// Selected item.
    /// </summary>
    [Parameter]
    public Guid? Id { get; set; }

    private bool DeleteTarget;
    private ParametersView? ParametersView;
    private List<IGXParameter> Parameters = new List<IGXParameter>();
    NetworkSettings? networkSettings;
    SerialSettings? serialSettings;

    List<Gurux.DLMS.Enums.InterfaceType> InterfaceList = new List<Gurux.DLMS.Enums.InterfaceType>();
    Dictionary<string, string> MediaList = new Dictionary<string, string>();
    private Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings? _settings;
    private Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings? _templateSettings;

    Gurux.DLMS.Enums.InterfaceType InterfaceType
    {
        get
        {
            if (_settings != null)
            {
                return (Gurux.DLMS.Enums.InterfaceType)_settings.InterfaceType;
            }
            return Gurux.DLMS.Enums.InterfaceType.HDLC;
        }
        set
        {
            if (_settings != null)
            {
                _settings.InterfaceType = (int)value;
            }
        }
    }

    string? IsCreate()
    {
        if (action == CrudAction.Create)
        {
            return null;
        }
        return "disabled";
    }

    bool IsAuthenticated()
    {
        if (_settings == null ||
            !(_settings.Authentication == (byte)Gurux.DLMS.Enums.Authentication.None ||
            _settings.Authentication == (byte)Gurux.DLMS.Enums.Authentication.HighGMAC ||
            _settings.Authentication == (byte)Gurux.DLMS.Enums.Authentication.HighECDSA))
        {
            return false;
        }
        return true;
    }

    bool IsSecured()
    {
        if (_settings == null ||
            _settings.Security != (byte)Gurux.DLMS.Enums.Security.None)
        {
            return false;
        }
        return true;
    }

    private bool IsClientSystemTileDisabled()
    {
        if (_settings == null ||
           _settings.Security == (byte)Gurux.DLMS.Enums.Security.None ||
            _settings.Authentication != (byte)Gurux.DLMS.Enums.Authentication.HighGMAC)
        {
            return true;
        }
        return false;
    }

    /// <summary>
    /// Authentication level is read from the device template.
    /// </summary>
    string? Authentication
    {
        get
        {
            if (_templateSettings == null)
            {
                return null;
            }
            return Convert.ToString((Gurux.DLMS.Enums.Authentication)_templateSettings.Authentication);
        }
        set
        {

        }
    }

    string? Password
    {
        get
        {
            if (_settings != null && _settings.IsHex)
            {
                return GXDLMSTranslator.ToHex(_settings.HexPassword);
            }
            return _settings?.Password;
        }
        set
        {
            if (_settings != null)
            {
                if (_settings.IsHex)
                {
                    _settings.HexPassword = GXDLMSTranslator.HexToBytes(value);
                    _settings.Password = null;
                }
                else
                {
                    _settings.Password = value;
                    _settings.HexPassword = null;
                }
            }
        }
    }

    private Gurux.DLMS.AMI.Shared.DTOs.Manufacturer.GXManufacturer? _manufacturer;
    private Gurux.DLMS.AMI.Shared.DTOs.Manufacturer.GXDeviceModel? _model;
    private Gurux.DLMS.AMI.Shared.DTOs.Manufacturer.GXDeviceVersion? _version;
    private Gurux.DLMS.AMI.Shared.DTOs.Manufacturer.GXDeviceSettings? _deviceSettings;

    /// <summary>
    /// Selected manufacturer.
    /// </summary>
    private Guid? SelectedManufacturer
    {
        get
        {
            if (_manufacturer == null)
            {
                return null;
            }
            return _manufacturer.Id;
        }
        set
        {
            if (Parent?.Manufacturers != null && _manufacturer?.Id != value)
            {
                _manufacturer = null;
                foreach (var manufacturer in Parent.Manufacturers)
                {
                    if (manufacturer.Id == value)
                    {
                        _manufacturer = manufacturer;
                        if (manufacturer.Models != null && manufacturer.Models.Any())
                        {
                            SelectedModel = manufacturer.Models[0].Id;
                        }
                        else
                        {
                            _model = null;
                            _version = null;
                            _deviceSettings = null;
                            StateHasChanged();
                        }
                        break;
                    }
                }
                if (value == Guid.Empty)
                {
                    SelectFirstCustomTemplate();
                }
            }
        }
    }

    /// <summary>
    /// Selected model.
    /// </summary>
    private Guid? SelectedModel
    {
        get
        {
            if (_model == null)
            {
                return null;
            }
            return _model.Id;
        }
        set
        {
            if (_manufacturer?.Models != null && _model?.Id != value)
            {
                _model = null;
                foreach (var model in _manufacturer.Models)
                {
                    if (model.Id == value)
                    {
                        _model = model;
                        if (_model?.Versions != null && _model.Versions.Any())
                        {
                            SelectedVersion = _model.Versions[0].Id;
                        }
                        else
                        {
                            _version = null;
                            StateHasChanged();
                        }
                        break;
                    }
                }
            }
        }
    }

    /// <summary>
    /// Selected version.
    /// </summary>
    private Guid? SelectedVersion
    {
        get
        {
            if (_version == null)
            {
                return null;
            }
            return _version.Id;
        }
        set
        {
            if (_model?.Versions != null && _model.Id != value)
            {
                _version = null;
                foreach (var version in _model.Versions)
                {
                    if (version.Id == value)
                    {
                        _version = version;
                        if (_version?.Settings != null && _version.Settings.Any())
                        {
                            SelectedSettings = _version.Settings[0].Id;
                        }
                        else
                        {
                            _deviceSettings = null;
                            StateHasChanged();
                        }
                        break;
                    }
                }
            }
        }
    }

    /// <summary>
    /// Selected settings.
    /// </summary>
    private Guid? SelectedSettings
    {
        get
        {
            if (_deviceSettings == null)
            {
                //Guid.Empty is returned for the custom templates.
                return Guid.Empty;
            }
            return _deviceSettings.Id;
        }
        set
        {
            if (_version?.Settings != null && _deviceSettings?.Id != value)
            {
                _deviceSettings = null;
                foreach (var settings in _version.Settings)
                {
                    if (settings.Id == value)
                    {
                        _deviceSettings = settings;
                        if (Active != null && Parent?.Templates != null && _deviceSettings.Template != null)
                        {
                            Active.Template = null;
                            foreach (var it in Parent.Templates)
                            {
                                if (it.Id == _deviceSettings.Template.Id)
                                {
                                    Active.Template = it;
                                }
                            }
                            if (Active.Template != null)
                            {
                                Active.WaitTime = Active.Template.WaitTime;
                                Active.ResendCount = Active.Template.ResendCount;
                                Active.MediaType = Active.Template.MediaType;
                                Active.MediaSettings = Active.Template.MediaSettings;
                                Active.Settings = Active.Template.Settings;
                                //Update template settings when settings are selected for the first time.
                                if (!string.IsNullOrEmpty(Active.Settings))
                                {
                                    //Get device template settings when device is selected for the first time.
                                    _settings = _templateSettings = JsonSerializer.Deserialize<Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings>(Active.Settings);
                                }
                            }
                        }
                        StateHasChanged();
                        break;
                    }
                }
            }
        }
    }


    private string? SelectedTemplate
    {
        get
        {
            if (Active == null || Active.Template == null)
            {
                return null;
            }
            return Active.Template.Name;
        }
        set
        {
            if (Active != null && Parent?.Templates != null)
            {
                foreach (GXDeviceTemplate it in Parent.Templates)
                {
                    if (it.Name == value)
                    {
                        Active.Template = it;
                        Active.WaitTime = it.WaitTime;
                        Active.ResendCount = it.ResendCount;
                        Active.MediaType = it.MediaType;
                        Active.MediaSettings = it.MediaSettings;
                        Active.Settings = it.Settings;
                        //Update template settings.
                        _settings = _templateSettings = JsonSerializer.Deserialize<Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings>(Active.Template.Settings);
                        //Update interfaces.
                        if (_templateSettings?.Profiles != null && _templateSettings.Profiles.Any())
                        {
                            InterfaceList.Clear();
                            foreach (var p in _templateSettings.Profiles)
                            {
                                InterfaceList.Add((Gurux.DLMS.Enums.InterfaceType)p.InterfaceType);
                            }
                        }
                        else
                        {
                            InterfaceList = new List<Gurux.DLMS.Enums.InterfaceType>(new[]{
                            Gurux.DLMS.Enums.InterfaceType.HDLC,
                            Gurux.DLMS.Enums.InterfaceType.HdlcWithModeE,
                            Gurux.DLMS.Enums.InterfaceType.WRAPPER});
                        }
                        break;
                    }
                }
            }
        }
    }

    /// <summary>
    /// Copy meter settings.
    /// </summary>
    /// <param name="target"></param>
    /// <param name="source"></param>
    public static void Copy(GXDevice target, GXDeviceTemplate source)
    {
        target.WaitTime = source.WaitTime;
        target.ResendCount = source.ResendCount;
        target.Settings = source.Settings;
        target.Name = source.Name;
        target.MediaType = source.MediaType;
        target.MediaSettings = source.MediaSettings;
    }

    protected override async Task OnInitializedAsync()
    {
        MediaList.Add(typeof(GXNet).FullName, Properties.Resources.Network);
        MediaList.Add(typeof(GXSerial).FullName, Properties.Resources.SerialPort);
        HttpResponseMessage response;
        try
        {
            Notifier.ProgressStart();
            Notifier.ClearStatus();
            Notifier.Clear();
            if (Active == null && Id != null)
            {
                //Get device data.
                var tmp = (await Http.GetAsJsonAsync<GetDeviceResponse>(string.Format("api/Device?id={0}", Id)));
                if (tmp?.Item != null)
                {
                    _active = tmp.Item;
                }
                else
                {
                    NavigationManager.NavigateTo("404");
                }
            }
            if (Active == null)
            {
                throw new ArgumentException(Properties.Resources.InvalidTarget);
            }
            action = ClientHelpers.GetAction(Action);
            InterfaceList = new List<Gurux.DLMS.Enums.InterfaceType>(new[]{
                Gurux.DLMS.Enums.InterfaceType.HDLC,
                Gurux.DLMS.Enums.InterfaceType.HdlcWithModeE,
                Gurux.DLMS.Enums.InterfaceType.WRAPPER,
                Gurux.DLMS.Enums.InterfaceType.WiredMBus,
                Gurux.DLMS.Enums.InterfaceType.WirelessMBus,
                Gurux.DLMS.Enums.InterfaceType.Plc,
                Gurux.DLMS.Enums.InterfaceType.PlcHdlc,
                Gurux.DLMS.Enums.InterfaceType.LPWAN,
                Gurux.DLMS.Enums.InterfaceType.WiSUN,
                Gurux.DLMS.Enums.InterfaceType.PlcPrime,
                Gurux.DLMS.Enums.InterfaceType.SMS
    });
            switch (action)
            {
                case CrudAction.Create:
                    try
                    {
                        //Get default user settings.
                        GetUserSettings us = await Http.GetAsJsonAsync<GetUserSettings>("api/UserSetting?Name=Ami.Device");
                        if (us.Item != null && !string.IsNullOrEmpty(us.Item.Value))
                        {
                            Guid Id = JsonSerializer.Deserialize<Guid>(us.Item.Value);
                            //Get device data.
                            GetDeviceResponse ret = await Http.GetAsJsonAsync<GetDeviceResponse>(string.Format("api/Device?id={0}", Id));
                            if (ret.Item != null)
                            {
                                _active = ret.Item;
                                Active.Id = Guid.Empty;
                                Active.Name = null;
                            }
                        }

                        GXObject? ldn = Active?.Objects?.Where(w => w.Template.LogicalName == "0.0.42.0.0.255").SingleOrDefault() as GXObject;
                        if (ldn == null)
                        {
                            if (Active.Objects == null)
                            {
                                Active.Objects = new List<GXObject>();
                            }
                            GXAttribute att = new()
                                {
                                    Template = new GXAttributeTemplate()
                                    {
                                        Index = 2
                                    }
                                };
                            Active.Objects.Add(new GXObject()
                                {
                                    Template = new GXObjectTemplate()
                                    {
                                        LogicalName = "0.0.42.0.0.255"
                                    },
                                    Attributes = new List<GXAttribute>(new GXAttribute[] { att })
                                }
                            );
                        }
                    }
                    catch (Exception)
                    {
                        //It's OK if this fails.
                    }
                    break;
                case CrudAction.Update:
                    break;
                case CrudAction.Delete:
                    break;
                default:
                    ClientHelpers.NavigateToLastPage(NavigationManager, Notifier);
                    return;
            }
            Notifier.Clear();
            if (action == CrudAction.Delete)
            {
                Notifier.AddMenuItem(new GXMenuItem() { Text = Properties.Resources.Remove, Icon = "oi oi-trash", OnClick = OnSave });
            }
            else
            {
                Notifier.AddMenuItem(new GXMenuItem() { Text = Properties.Resources.Save, Icon = "oi oi-pencil", OnClick = OnSave });
            }
            Notifier.AddMenuItem(new GXMenuItem() { Text = Properties.Resources.Cancel, Icon = "oi oi-action-undo", OnClick = OnCancel });
            Notifier.UpdateButtons();
            Update();
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
        finally
        {
            Notifier?.ProgressEnd();
        }
    }

    /// <summary>
    /// Validate the device content.
    /// </summary>
    /// <param name="sender"></param>
    private void OnValidate(GXValidator sender)
    {
        if (Active != null)
        {
            if (string.IsNullOrEmpty(Active.Name))
            {
                sender.AddError(() => Active.Name, "Invalid name.");
            }
            //Check password.
            if (!string.IsNullOrEmpty(Active.Settings))
            {
                var settings = JsonSerializer.Deserialize<Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings>(Active.Settings);
                if (settings != null && settings.Authentication != (byte)Gurux.DLMS.Enums.Authentication.None &&
                settings.Authentication != (byte)Gurux.DLMS.Enums.Authentication.HighGMAC &&
                settings.Authentication != (byte)Gurux.DLMS.Enums.Authentication.HighECDSA)
                {
                    if (action == CrudAction.Create && string.IsNullOrEmpty(Password))
                    {
                        sender.AddError(() => Password, "Invalid password.");
                    }
                }
            }

            //Check security settings
            if (_settings != null)
            {
                if (_settings.PreEstablished && string.IsNullOrEmpty(_settings.DeviceSystemTitle))
                {
                    sender.AddError(() => _settings.DeviceSystemTitle, Properties.Resources.InvalidSystemTitle);
                }
                if (_settings.Security != (byte)Enums.Security.None)
                {
                    if (!string.IsNullOrEmpty(_settings.ClientSystemTitle) && _clientSystemTitle != null)
                    {
                        if (_clientSystemTitle.GetByteArray().Length != 8)
                        {
                            sender.AddError(() => _clientSystemTitle, Properties.Resources.InvalidSystemTitle);
                        }
                    }
                    else
                    {
                        sender.AddError(() => _settings.ClientSystemTitle, Properties.Resources.InvalidSystemTitle);
                    }
                    if (!string.IsNullOrEmpty(_settings.DeviceSystemTitle) && _deviceSystemTitle != null)
                    {
                        if (_deviceSystemTitle.GetByteArray().Length != 8)
                        {
                            sender.AddError(() => _deviceSystemTitle, Properties.Resources.InvalidSystemTitle);
                        }
                    }
                    if (!string.IsNullOrEmpty(_settings.BlockCipherKey) && _blockCipherKey != null)
                    {
                        var bytes = _blockCipherKey.GetByteArray();
                        if ((_settings.SecuritySuite != (int)Gurux.DLMS.Objects.Enums.SecuritySuite.Suite2 && bytes.Length != 16) ||
                        (_settings.SecuritySuite == (int)Gurux.DLMS.Objects.Enums.SecuritySuite.Suite2 && bytes.Length != 32))
                        {
                            sender.AddError(() => _blockCipherKey, Properties.Resources.InvalidBlockCipherKey);
                        }
                    }
                    else
                    {
                        sender.AddError(() => _settings.BlockCipherKey, Properties.Resources.InvalidBlockCipherKey);
                    }
                    if (!string.IsNullOrEmpty(_settings.AuthenticationKey) && _authenticationKey != null)
                    {
                        var bytes = _authenticationKey.GetByteArray();
                        if ((_settings.SecuritySuite != (int)Gurux.DLMS.Objects.Enums.SecuritySuite.Suite2 && bytes.Length != 16) ||
                        (_settings.SecuritySuite == (int)Gurux.DLMS.Objects.Enums.SecuritySuite.Suite2 && bytes.Length != 32))
                        {
                            sender.AddError(() => _authenticationKey, Properties.Resources.InvalidAuthenticationKey);
                        }
                    }
                    else
                    {
                        sender.AddError(() => _settings.AuthenticationKey, Properties.Resources.InvalidAuthenticationKey);
                    }
                }
            }
            if (!Active.Dynamic.GetValueOrDefault())
            {
                if (networkSettings != null && Active.MediaType == "Gurux.Net.GXNet")
                {
                    networkSettings.Validate(sender);
                }
                else if (serialSettings != null && Active.MediaType == "Gurux.Serial.GXSerial")
                {
                    serialSettings.Validate(sender);
                }
            }
        }
    }

    /// <summary>
    /// Save device.
    /// </summary>
    public async void OnSave()
    {
        try
        {
            if (Active == null)
            {
                throw new Exception(Properties.Resources.InvalidTarget);
            }
            _validator?.ClearErrors();
            if (_validator != null && !_validator.Validate())
            {
                StateHasChanged();
                return;
            }
            Notifier.ProgressStart();
            Notifier.ClearStatus();
            //Update device parameters.
            ParametersView?.UpdateSettings<GXDeviceParameter>(Active.Parameters);
            var options = new JsonSerializerOptions
                {
                    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
                };
            if (_settings != null)
            {
                _settings.ClientSystemTitle = GXDLMSTranslator.ToHex(_clientSystemTitle?.GetByteArray(), false);
                _settings.DeviceSystemTitle = GXDLMSTranslator.ToHex(_deviceSystemTitle?.GetByteArray(), false);
                _settings.BlockCipherKey = GXDLMSTranslator.ToHex(_blockCipherKey?.GetByteArray(), false);
                _settings.AuthenticationKey = GXDLMSTranslator.ToHex(_authenticationKey?.GetByteArray(), false);
                Active.Settings = JsonSerializer.Serialize(_settings, options);
            }
            if (action == CrudAction.Delete)
            {
                await Http.PostAsJson<RemoveDeviceResponse>("api/Device/Delete", new RemoveDevice()
                    {
                        Ids = new Guid[] { Active.Id },
                        Delete = DeleteTarget
                    });
            }
            else if (action == CrudAction.Create)
            {
                await Http.PostAsJson<UpdateDeviceResponse>("api/Device/Add", new UpdateDevice()
                    {
                        Devices = new GXDevice[] { Active },
                        LateBinding = !CreateImmediately
                    });
            }
            else if (action == CrudAction.Update)
            {
                await Http.PostAsJson<UpdateDeviceResponse>("api/Device/Update", new UpdateDevice()
                    {
                        Devices = new GXDevice[] { Active }
                    });
            }
            ClientHelpers.NavigateToLastPage(NavigationManager, Notifier);
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
        finally
        {
            Notifier?.ProgressEnd();
        }
    }

    /// <summary>
    /// Save current settings as a default settings.
    /// </summary>
    public async Task OnDefault()
    {
        try
        {
            if (Active != null && Active.Id != Guid.Empty)
            {
                //Get default user settings.
                GXUserSetting? settings;
                GetUserSettings? tmp = await Http.GetAsJsonAsync<GetUserSettings>("api/UserSetting?Name=Ami.Device");
                if (tmp?.Item == null)
                {
                    settings = new GXUserSetting();
                    settings.Name = "Ami.Device";
                }
                else
                {
                    settings = tmp.Item;
                }
                settings.Value = JsonSerializer.Serialize(Active.Id);
                await Http.PostAsJson("api/UserSetting/Add", new GXUserSetting[] { settings });
            }
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
    }

    private void OnCancel()
    {
        if (Notifier != null)
        {
            try
            {
                ParametersView?.Cancel();
            }
            catch (Exception ex)
            {
                Notifier.ProcessError(ex);
            }
            ClientHelpers.NavigateToLastPage(NavigationManager, Notifier);
        }
    }

    public void Dispose()
    {
    }
}
